// 计算属性解决模板中复杂的逻辑运算及复用的问题
// 计算属性只有在内部依赖发生改变的时候才能调用
// 计算属性会缓存上一次依赖算出来的值
// 多次复用相同的属性，计算属性只调用一次

var Vue = (function () {
    var _reg_compiler = /\{\{(.+?)\}\}/g,
        computedData = {},
        dataPool = {};

    function Vue(options) {
        this.$el = document.querySelector(options.el);
        this.$data = options.data();
        this._init(this, options.computed, options.template)
    }

    Vue.prototype._init = function (vm, computed, template) {
        dataReactive(vm);
        computedReactive(vm, computed);
        render(vm, template);
    }

    function render(vm, template) {
        var container = document.createElement('div'),
            _el = vm.$el;
        container.innerHTML = template;
        var domTree = _compileTemplate(vm, container);
        _el.appendChild(domTree);
    }

    function _compileTemplate(vm, container) {
        var oAllElements = container.getElementsByTagName('*'),
            oItem = null;
        for (var i = 0; i < oAllElements.length; i++) {
            oItem = oAllElements[i];
            var matched = oItem.textContent.match(_reg_compiler);
            if (matched) {
                oItem.textContent = oItem.textContent.replace(_reg_compiler, function (node, key) {
                    dataPool[key.trim()] = oItem;
                    return vm[key.trim()];
                })
            }
        }
        return container;
    }


    function dataReactive(vm) {
        var _data = vm.$data;
        for (var key in _data) {
            (function (key) {
                Object.defineProperty(vm, key, {
                    get: function () {
                        return _data[key];
                    },
                    set: function (newValue) {
                        _data[key] = newValue;
                        update(vm, key);
                        _updateComputedData(vm, key, function (vm, key) {
                            update(vm, key);
                        })
                    }
                })
            })(key)
        }
    }

    function update(vm, key) {
        dataPool[key].textContent = vm[key];
    }

    function computedReactive(vm, computed) {
        _initComputedData(vm, computed);
        for (var key in computedData) {
            (function (key) {
                Object.defineProperty(vm, key, {
                    get() {
                        return computedData[key].value;
                    },
                    set(newValue) {
                        computedData[key].value = newValue;
                    }
                })
            })(key);
        }
    }

    /**
     * total:{
     *  value:函数执行返回的结果
     *  get:get
     *  dep:['a','b]
     * }
    */
    function _initComputedData(vm, computed) {
        for (var key in computed) {
            var descriptor = Object.getOwnPropertyDescriptor(computed, key),
                descriptionrFn = descriptor.value.get ? descriptor.value.get : descriptor.value;
            computedData[key] = {};
            computedData[key].value = descriptionrFn.call(vm);
            computedData[key].get = descriptionrFn.bind(vm);
            computedData[key].dep = _collectDep(descriptionrFn);
        }
    }

    function _collectDep(fn) {
        var _collection = fn.toString().match(/this.(.+?)/g);
        if (_collection.length > 0) {
            for (var i = 0; i < _collection.length; i++) {
                _collection[i] = _collection[i].split('.')[1];
            }
        }
        return _collection;
    }


    function _updateComputedData(vm, key, updatefn) {
        var _dep = null;
        for (var _key in computedData) {
            _dep = computedData[_key].dep;
            for (var i = 0; i < _dep.length; i++) {
                if (_dep[i] === key) {
                    vm[_key] = computedData[_key].get();
                    updatefn(vm, _key);
                }
            }
        }
    }

    return Vue;
})()

export default Vue;